from llvmlite import ir

from MelodieAllegro.types import double, integer, i32
from MelodieAllegro.exteral_functions.builtin_func_types import builtins


def create_structure(module: ir.Module, builder: ir.IRBuilder, functions):
    structure_type = ir.LiteralStructType([double, double])
    c = ir.Constant(structure_type,
                    (
                        ir.Constant(double, 123.0), ir.Constant(double, 16.9989)
                    ))
    structure_ptr = builder.alloca(structure_type, name='structure')
    builder.store(c, structure_ptr)
    extracted = builder.extract_value(builder.load(structure_ptr), 1, name='extracted')
    c_inserted = builder.insert_value(builder.load(structure_ptr), ir.Constant(double, 1024.201), 1, name="c")
    extracted_after = builder.extract_value(c_inserted, 1, name='extracted_after')
    print("allocate structure")
    builder.call(functions['print'], [extracted])
    builder.call(functions['print'], [extracted_after])

    # const_ptr = ir.Constant(structure_type.as_pointer(), None)
    # # builder.store(c, const_ptr)
    # builder.call(functions['print'], [extracted_after])
    # # c2 = const_ptr.gep([ir.Constant(i32, 0), ir.Constant(i32, 0)])
    # c2 = const_ptr.gep([ir.Constant(i32, 0), ir.Constant(i32, 0)])
    val = builder.gep(structure_ptr, [ir.Constant(i32, 0), ir.Constant(i32, 0)])
    # c2_val = builder.load(c2, name='c2_val')
    builder.call(functions['print'], [builder.load(val)])


def create_vector(builder: ir.IRBuilder, functions):
    vecty = ir.VectorType(double, 2)
    vec = ir.Constant(vecty, ir.Undefined)
    idxty = ir.IntType(32)
    a = ir.Constant(double, 1.0)
    b = ir.Constant(double, 22222.0)
    vec = builder.insert_element(vec, a, idxty(0), name='vec1')
    vec = builder.insert_element(vec, b, idxty(1), name='vec2')
    c = builder.extract_element(vec, idxty(1), name='extracted_c')
    builder.call(functions['print'], [c])


def create_array(builder: ir.IRBuilder, functions):
    tp = ir.ArrayType(double, 3)
    c = ir.Constant(tp, (double(0.9), double(2.1), double(3.9)))
    arr_ptr = builder.alloca(tp, name='arr')
    builder.store(c, arr_ptr)
    actual = builder.gep(arr_ptr, [ir.Constant(i32, 0), ir.Constant(i32, 2)])
    builder.call(functions['print'], [builder.load(actual)])


def create_array_structural(builder: ir.IRBuilder, functions):
    structure_type = ir.LiteralStructType([double, double])
    tp = ir.ArrayType(structure_type, 1)
    c = ir.Constant(tp, (
        (double(0.9), double(2.1)),
    ))
    arr_ptr = builder.alloca(tp, name='arr')
    builder.store(c, arr_ptr)
    actual = builder.gep(arr_ptr, [ir.Constant(i32, 0), ir.Constant(i32, 0), ir.Constant(i32, 1)])
    builder.call(functions['print'], [builder.load(actual)])


def create_structural_vector(builder: ir.IRBuilder, functions):
    structure_type = ir.LiteralStructType([double, double])
    vecty = ir.VectorType(structure_type, 2)
    vec = ir.Constant(vecty, ir.Undefined)
    idxty = ir.IntType(32)
    a = ir.Constant(structure_type,
                    (
                        ir.Constant(double, 99999123.0),
                        ir.Constant(double, 169999.9989)
                    ))
    b = ir.Constant(structure_type,
                    (
                        ir.Constant(double, 1222223.0),
                        ir.Constant(double, 11231236.12)
                    ))

    vec = builder.insert_element(vec, a, idxty(0), name='vec1')
    vec = builder.insert_element(vec, b, idxty(1), name='vec2')
    c = builder.extract_element(vec, idxty(1), name='extracted_c')
    d = builder.extract_value(c, 0, name='extracted')
    builder.call(functions['print'], [d])


def create_array_on_heap(builder: ir.IRBuilder, functions):
    structure_type = ir.LiteralStructType([double, double])
    tp = ir.ArrayType(structure_type, 1)
    c = ir.Constant(tp, (
        (double(0.9), double(2.1)),
    ))
    arr_ptr = builder.alloca(tp, name='arr')
    builder.store(c, arr_ptr)
    actual = builder.gep(arr_ptr, [ir.Constant(i32, 0), ir.Constant(i32, 0), ir.Constant(i32, 1)])
    builder.call(functions['print'], [builder.load(actual)])

    builtins.runtime_init.call(builder, [])

    ret = builtins.malloc.call(builder, [integer(64)], name="aaa")
    casted = builder.bitcast(ret, ir.PointerType(double), name="doubles")

    builder.store(double(555), casted)
    ep = builder.gep(casted, [i32(1)], name="ep")
    builder.store(double(666), ep)

    # builder.sto
    extracted = builder.gep(casted, [i32(0)], name="extracted")
    extracted_666 = builder.gep(casted, [i32(1)], name="extracted")
    builder.call(functions['print'], [builder.load(extracted)])
    builder.call(functions['print'], [builder.load(extracted_666)])

    builtins.print_double.call(builder, [builder.load(extracted)])
